---
title: Editing feature layers
description: This sample uses the Leaflet <a href="//github.com/Leaflet/Leaflet.Editable">Editable plugin</a> to help users manipulate the geometry of features from a hosted feature service and pass the edits back to the server.

layout: example.hbs
legacyLeaflet: 1.0.3

---
<script src="https://unpkg.com/leaflet.path.drag@0.0.5"></script>
<script src="https://unpkg.com/leaflet-editable@1.0.0"></script>

<div id='map'></div>

<script type="text/javascript">
    // make sure double clicking the map *only* triggers the editing workflow
    var map = L.map('map', {
      editable: true,
      doubleClickZoom: false
    }).setView([45.512, -122.619], 12);

    var tilelayer = L.esri.basemapLayer("Topographic").addTo(map);

    // create a feature layer and add it to the map
    var pedestrianDistricts = L.esri.featureLayer({
      url: 'https://services.arcgis.com/rOo16HdIMeOBI4Mb/arcgis/rest/services/PDX_Pedestrian_Districts/FeatureServer/0'
    }).addTo(map);

    // create a generic control to invoke editing
    L.EditControl = L.Control.extend({
        options: {
            position: 'topleft',
            callback: null,
            kind: '',
            html: ''
        },
        // when the control is added to the map, wire up its DOM dynamically and add a click listener
        onAdd: function (map) {
            var container = L.DomUtil.create('div', 'leaflet-control leaflet-bar'),
                link = L.DomUtil.create('a', '', container);
            link.href = '#';
            link.title = 'Create a new ' + this.options.kind;
            link.innerHTML = this.options.html;
            L.DomEvent.on(link, 'click', L.DomEvent.stop)
                      .on(link, 'click', function () {
                        window.LAYER = this.options.callback.call(map.editTools);
                      }, this);
            return container;
        }
    });

    // extend the control to draw polygons
    L.NewPolygonControl = L.EditControl.extend({
        options: {
            position: 'topleft',
            callback: map.editTools.startPolygon,
            kind: 'polygon',
            html: '▰'
        }
    });

    // extend the control to draw rectangles
    L.NewRectangleControl = L.EditControl.extend({
        options: {
            position: 'topleft',
            callback: map.editTools.startRectangle,
            kind: 'rectangle',
            html: '⬛'
        }
    });

    // add the two new controls to the map
    map.addControl(new L.NewPolygonControl());
    map.addControl(new L.NewRectangleControl());

    // when users CMD/CTRL click an editable feature, remove it from the map and delete it from the service
    pedestrianDistricts.on('click', function (e) {
      if ((e.originalEvent.ctrlKey || e.originalEvent.metaKey) && e.layer.editEnabled()) {
        e.layer.editor.deleteShapeAt(e.latlng);
        // delete expects an id, not the whole geojson object
        pedestrianDistricts.deleteFeature(e.layer.feature.id);
      }
    });

    // when users double click a graphic toggle its editable status
    // when deselecting, pass the geometry update to the service
    pedestrianDistricts.on('dblclick', function (e) {
      e.layer.toggleEdit();
      if (!e.layer.editEnabled()) {
        pedestrianDistricts.updateFeature(e.layer.toGeoJSON());
      }
    });

    // when a new feature is drawn using one of the custom controls, pass the edit to the service
    map.on('editable:drawing:commit', function (e) {
      pedestrianDistricts.addFeature(e.layer.toGeoJSON());
      e.layer.toggleEdit();
    });
</script>